<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Bedarfsangepasste Widgets in PyQt4</title>
<link rel="stylesheet" href="../format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="PyQt, PyQt4, custom widgets, tutorial, Python, Linux">
<meta nam="description" content="Bedarfsangepasste Widgets in PyQt4">
<meta name="language" content="de">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">
<meta name="translation" content="manuel stein">
</head>

<body>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href=".." title="Home">Contents</a>


<h1>Bedarfsangepasste Widgets in PyQt4</h1>

<p>
Haben Sie sich jemals einen Anwendung angesehen und sich gefragt, wie
ein bestimmtes Item der grafischen Oberfläche erzeugt wurde?
Wahrscheinlich ist das jedem Programmieranfänger schon mal so
gegangen. Sie suchen danach in einer Auflistung aller Widgets Ihrer
bevorzugten Bibliothek für Benutzeroberflächen, aber können dort
nichts entsprechendes finden. Toolkits bieten gewöhnlich nur die meist
verbreiteten Widgets wie Knöpfe, Textwidgets, Regler
<acronym title="und so weiter">usw.</acronym> an. Kein Toolkit kann
Ihnen jedes denkbare Widget anbieten.
</p>
<p>
Es gibt im Grund zwei Arten von Toolkits: Superleicht-Toolkits und
Schwergewichts-Toolkits. Ein Besipiel für die sparsamere Variante ist
das FLTK-Toolkit. Es bringt nur die ganz grundlegenden Widgets mit und
setzt voraus, dass der Programmierer sich die komplexeren selber
zusammenstellt. PyQt4 hingegen gehört zu den Schwergewichten. Es hat
einen ganzen Haufen Widgets. Dennoch kann es nicht alle
Spezial-Widgets abdecken. Ein Geschwindigkeitsmesser-Widget zum
Beispiel oder ein Widget, das die Speicherkapazität einer zu
brennenden CD misst.
</p>
<p>
Programmierer müssen sich solche Widgets selber basteln. Sie erreichen
dies, indem sie die Malwerkzeuge des Toolkits einsetzen. Dabei kann
man zwei mögliche Wege einschlagen: Entweder der Programmierer
modifiziert oder erweiter ein bereits existierendes Widget oder er
erzeugt ein komplett eigenes.
</p>


<h3>Brenn-Widget</h3>

<p>
Hierbei handelt es sich um ein Widget, wie wir es in Nero, K3B oder
anderen CD/DVD-Brennprogrammen vorfinden können.
</p>


<pre class="code">
#!/usr/bin/python

# burning.py

import sys
from PyQt4 import QtGui, QtCore


class Widget(QtGui.QLabel):
    def __init__(self, parent):
        QtGui.QLabel.__init__(self, parent)
        self.setMinimumSize(1, 30)
        self.parent = parent
        self.num = [75, 150, 225, 300, 375, 450, 525, 600, 675]

    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)

        font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
        paint.setFont(font)

        size = self.size()
        w = size.width()
        h = size.height()
        cw = self.parent.cw
        step = int(round(w / 10.0))


        till = int(((w / 750.0) * cw))
        full = int(((w / 750.0) * 700))

        if cw >= 700:
            paint.setPen(QtGui.QColor(255, 255, 255))
            paint.setBrush(QtGui.QColor(255, 255, 184))
            paint.drawRect(0, 0, full, h)
            paint.setPen(QtGui.QColor(255, 175, 175))
            paint.setBrush(QtGui.QColor(255, 175, 175))
            paint.drawRect(full, 0, till-full, h)
        else:
            paint.setPen(QtGui.QColor(255, 255, 255))
            paint.setBrush(QtGui.QColor(255, 255, 184))
            paint.drawRect(0, 0, till, h)


        pen = QtGui.QPen(QtGui.QColor(20, 20, 20), 1, QtCore.Qt.SolidLine)
        paint.setPen(pen)
        paint.setBrush(QtCore.Qt.NoBrush)
        paint.drawRect(0, 0, w-1, h-1)

        j = 0

        for i in range(step, 10*step, step):
            paint.drawLine(i, 0, i, 5)
            metrics = paint.fontMetrics()
            fw = metrics.width(str(self.num[j]))
            paint.drawText(i-fw/2, h/2, str(self.num[j]))
            j = j + 1

        paint.end()

class Burning(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.cw = 75

        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
        self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
        self.slider.setRange(1, 750)
        self.slider.setValue(75)
        self.slider.setGeometry(30, 40, 150, 30)

        self.wid = Widget(self)

        self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.wid)
        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        self.setGeometry(300, 300, 300, 220)
        self.setWindowTitle('Burning')

    def changeValue(self, event):
        self.cw = self.slider.value()
        self.wid.repaint()


app = QtGui.QApplication(sys.argv)
dt = Burning()
dt.show()
app.exec_()
</pre>

<p>
In unserem Beispiel verwenden wir einen <em>QSlider</em> und ein
selbsterzeugtes Widget. Der Regler steuert das erstellte Widget.
Dieses Widget zeigt grafisch die vollständige Kapazität eines
Speichermediums und den freien, noch verfügbaren Platz auf ihm an. Der
Minimalwert unseres erstellten Widgets ist 1, der maximale 750. Wenn
wir einen Wert von 700 erreichen, beginnen wir damit, in roter Farbe
zu zeichnen, was gewöhnlich das Überbrennen des Datenträgers anzeigt.
</p>

<p>
Das Brenn-Widget wir am Fuße des Fensters positioniert. Dies erreicht
man durch Verwendung einer <em>QHBoxLayout</em> und ein <em>QVBoxLayout</em>
</p>

<pre class="explanation">
 class Widget(QtGui.QLabel):
     def __init__(self, parent):
         QtGui.QLabel.__init__(self, parent)
</pre>

<p>
Das Brenn-Widget basiert auf dem <em>QLabel</em>-Widget.
</p>

<pre class="explanation">
 self.setMinimumSize(1, 30)
</pre>

<p>
Wir ändern die Minimalhöhe des Widgets. Der Standarwert ist wenig zu
klein für uns.
</p>

<pre class="explanation">
 font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
 paint.setFont(font)
</pre>

<p>
Wir nutzen einen kleinen Schrifttyp als Standardeinstellung, das
entspricht besser unseren Bedürfnissen.
</p>


<pre class="explanation">
 size = self.size()
 w = size.width()
 h = size.height()
 cw = self.parent.cw
 step = int(round(w / 10.0))

 till = int(((w / 750.0) * cw))
 full = int(((w / 750.0) * 700))
</pre>

<p>
Wir zeichnen das Widget dynamisch. Je größer das Fenster, desto größer
das Brenn-Widget und umgekehrt. Aus diesem Grund müssen wir die Größe
des Widget, auf die wir das Widget zeichnen, berechnen. Der till-Parameter 
bestimmt die zu zeichnende Gesamtgröße. Dieser Wert stammt vom Regler-Widget. 
Es handelt sich um eine Teilmenge der Gesamtfläche. Der full-Parameter 
bestimmt den Punkt, an dem wir mit rot zu zeichnen beginnen. Beachten Sie, 
dass wir Gleitkommazahlen verwenden, um eine höhere Genauigkeit zur erlangen.
</p>

<p>
Das tatsächliche Nachzeichnen geschieht in drei Schritten: Wir zeichnen das 
gelbe oder rote und gelbe Rechteck, danach die vertikalen Linien, die das Widget 
in die verschiedenen Abschnitte einteilen. Schließlich zeichnen wir die Zahlen, 
die die Speicherkapazität anzeigen.
</p>

<pre class="explanation">
 metrics = paint.fontMetrics()
 fw = metrics.width(str(self.num[j]))
 paint.drawText(i-fw/2, h/2, str(self.num[j]))
</pre>

<p>
Wir verwenden Schriftmetrik zum Zeichnen des Textes. Um den Text um die 
vertikale Liniie herum anordnen zu können, müssen wir seine Breite kennen.
</p>


<img src="../images/burning.jpg" alt="Das Brenn-Widget" />
<p class="figure">Abbildung: Das Brenn-Widget</p>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified September 28, 2007  <span class="copyright">&copy; 2007 - 2011 Jan Bodnar</span>
<span class="copyright">Translation © 2009 Manuel Stein</span>
</div>
</div>

</div> <!-- content -->
</body>
</html>
